# Reksio - Memory Map Editor
# Copyright (C) 2023 CERN
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# In applying this licence, CERN does not waive the privileges and immunities
# granted to it by virtue of its status as an Intergovernmental Organization or
# submit itself to any jurisdiction.


from upgrade_functions import *

UPGRADES = {
    'core': {
        None: upgrade_all_to_1_0_0,
        '1.0.0': upgrade_core_to_2_0_0,
        '2.0.0': upgrade_core_to_3_0_0
    },
    'x-map-info': {
        None: upgrade_x_map_info_to_1_0_0
    },
    'x-fesa': {
        '1.0.0': upgrade_x_fesa_to_2_0_0
    },
    'x-gena': {
        '1.0.0': upgrade_x_gena_to_2_0_0
    },
    'x-driver-edge': {
        '1.0.0': upgrade_x_driver_edge_to_2_0_0,
        '2.0.0': upgrade_x_driver_edge_to_3_0_0
    }
}


def is_update_needed(mem_map):
    schema_versions = mem_map.get('schema-version', {})
    for extension, upgrade_functions in UPGRADES.items():
        upgrade_function = upgrade_functions.get(schema_versions.get(extension, None), None)
        if upgrade_function is not None:
            return True
    return False


def upgrade(mem_map):
    schema_versions = mem_map.get('schema-version', {})
    for extension, upgrade_functions in UPGRADES.items():
        upgrade_function = upgrade_functions.get(schema_versions.get(extension, None), None)
        if upgrade_function is not None:
            # upgrading
            upgrade_function(mem_map)
            # call again
            upgrade(mem_map)
            #break


# Upgrading maps without the GUI
def main():
    import argparse
    import glob
    import fileutils
    from tree import TreeView
    from pathlib import Path

    parser = argparse.ArgumentParser()
    parser.add_argument("-f", "--file", help="Parse single file (and submaps)")
    parser.add_argument("-d", "--dir", help="Scan whole directory and update all .cheby files")
    parser.add_argument("-o", "--output-path", help="Output path where all converted files are saved.")
    args = parser.parse_args()

    if not args.file and not args.dir:
        raise ValueError('Input file or directory cannot be empty!')

    map_files = glob.glob(args.dir + "/**/*.cheby", recursive=True)
    map_files.extend(glob.glob(args.dir + "/**/*.xml", recursive=True))
    map_files.extend(glob.glob(args.dir + "/**/*.yaml", recursive=True))
    if not map_files:
        raise FileNotFoundError(f'Files .cheby not found in this directory: "{args.dir}" - abort.')

    for map_file in map_files:
        fpath = Path(map_file)
        extension = fpath.suffix
        if extension == '.xml':
            map_file = fileutils.convert_xml_to_cheby(map_file)

        data = fileutils.load_yaml_file(map_file)

        root_node = TreeView(yaml_tree=data, root_node='memory-map', global_root=True)
        root_node.filename = map_file

        submaps = fileutils.get_submaps(root_node)
        all_maps = submaps + [root_node]
        for mem_map in all_maps:
            if not is_update_needed(mem_map):
                logging.warn(f'Memory map "{mem_map.filename}" up-to-date - skip and continue....\n')
                continue

            logging.warn(f"Memory map '{mem_map.filename}' will be updated.\n A backup will be created with .bak extension.")
            upgrade(mem_map)

            mem_map_path = Path(mem_map.filename) # can be stored as a string
            mem_map.filename = mem_map.filename if not args.output_path else f'{args.output_path}/{mem_map_path.name}'
            fileutils.save(mem_map)

            logging.info(f"Update of {str(mem_map.filename)} succeeded.'")

if __name__ == "__main__":
    main()
